pub mod manager;

use std::{
    f64::EPSILON,
    net::IpAddr,
    sync::{Arc, Mutex, Weak},
};

/// The initial value of k in the algorithm
const INIT_K: f64 = 1.0;
/// A small epsilon value used for floating-point comparisons
const EPS: f64 = 1e-3;

/// Represents a server in the network.
#[derive(Clone)]
pub struct Server {
    ip: IpAddr,                                      // The IP address of the server
    neighbors: Arc<Mutex<Vec<Weak<Mutex<Server>>>>>, // A list of weak references to neighboring servers
    number: f64,                                     // A number representing the server's state
    task_enable: bool, // Flag indicating whether the server can gossip
    k: f64,            // Parameter for the gossiping algorithm
    k_step: f64,       // The factor by which k will change in each steps
}

impl Server {
    /// Creates a new `Server` instance with the given IP address and initial number.
    ///
    /// # Parameters
    /// - `ip`: The IP address of the server.
    /// - `number`: The initial number assigned to the server.
    ///
    /// # Returns
    /// An `Arc<Mutex<Self>>` containing the newly created server instance.
    pub fn new(ip: IpAddr, number: f64, k_step: f64) -> Arc<Mutex<Self>> {
        Arc::new(Mutex::new(Server {
            ip,
            neighbors: Arc::new(Mutex::new(Vec::new())),
            number,
            task_enable: true,
            k: INIT_K,
            k_step,
        }))
    }

    /// Adds a neighboring server to the list of neighbors.
    ///
    /// # Parameters
    /// - `neighbor`: A reference to the neighboring server to be added.
    pub fn add_neighbor(&mut self, neighbor: &Arc<Mutex<Server>>) {
        let mut neighbors = self.neighbors.lock().unwrap();
        neighbors.push(Arc::downgrade(neighbor));
    }

    /// Retrieves the current number of the server.
    ///
    /// # Returns
    /// The current number of the server.
    pub fn number(&self) -> f64 {
        self.number
    }

    /// Checks if the server's task is finished.
    ///
    /// # Returns
    /// `true` if the task is finished, otherwise `false`.
    pub fn finished(&self) -> bool {
        self.task_enable == false
    }

    /// Updates the value of k by multiplying it with the defined K_STEP.
    fn next_k(&mut self) {
        self.k *= self.k_step;
    }

    /// Selects the next server to gossip with, based on the algorithm's probability.
    ///
    /// # Returns
    /// An `Option<Rc<Mutex<Server>>>` containing the next server if available; `None` otherwise.
    fn next_server(&mut self) -> Option<Arc<Mutex<Server>>> {
        let neighbors = self.neighbors.lock().unwrap();
        if neighbors.is_empty() {
            return None; // No neighbors to gossip with
        }

        // Determine whether the task will continue based on the probability
        let cur_p = 1.0 / self.k + EPSILON; // Current probability threshold
        let p = rand::random::<f64>(); // Generate a random number
        if p < cur_p {
            // If the random number is less than the probability threshold
            let index = rand::random::<usize>() % neighbors.len(); // Select a random neighbor
            neighbors.get(index).and_then(Weak::upgrade) // Upgrade the weak reference to a strong reference
        } else {
            self.task_enable = false; // Disable the task if the probability condition fails
            None
        }
    }

    /// Performs the gossiping operation with a randomly selected neighbor.
    pub fn gossip(&mut self) {
        if !self.task_enable {
            // TODO
            // 1. add logging
            // 2. add enable/disable
            // println!(
            //     "[INFO] Server {} can't gossip, because the task is disabled",
            //     self.ip
            // );
            return; // Exit if gossiping is disabled
        }

        if let Some(neighbor) = self.next_server() {
            let mut neighbor = neighbor.lock().unwrap();
            if (neighbor.number - self.number).abs() < EPS {
                // If the neighbor's number is equal to this server's number
                // It means that the server has been visited
                self.next_k(); // Update k for this server
                return;
            }

            // Calculate the average number between this server and the neighbor
            let sum = self.number + neighbor.number;
            self.number = sum / 2.0;
            neighbor.number = sum / 2.0;

            // Reset the neighbor's k value and enable the task
            neighbor.k = INIT_K;
            neighbor.task_enable = true;
        }
    }
}

impl std::fmt::Debug for Server {
    /// Formats the server for debugging output.
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let neighbors_ips: Vec<IpAddr> = {
            let neighbors = self.neighbors.lock().unwrap(); // Lock the neighbors for reading
            neighbors
                .iter()
                .filter_map(|n| n.upgrade().map(|s| s.lock().unwrap().ip)) // Get IPs of upgraded neighbors
                .collect()
        };

        f.debug_struct("Server")
            .field("ip", &self.ip)
            .field("neighbors_ips", &neighbors_ips)
            .field("number", &self.number)
            .field("task_enable", &self.task_enable)
            .field("k", &self.k)
            .finish() // Format the server's details for debugging
    }
}
